Skip to content

feat: create terminal tiles on a git branch, in their own worktree - #145

Open
jpedrosa94 wants to merge 6 commits into
collabs-inc:devfrom
jpedrosa94:feat/branch-worktree-terminals
Open

feat: create terminal tiles on a git branch, in their own worktree#145
jpedrosa94 wants to merge 6 commits into
collabs-inc:devfrom
jpedrosa94:feat/branch-worktree-terminals

Conversation

@jpedrosa94

Copy link
Copy Markdown

Implements the tile-level slice of #66.

A new "New terminal on branch…" entry in both new-tile menus lets you pick a
branch and get a terminal running in that branch's own git worktree. Several
tiles can then sit on different branches of one repository at once, each with its
own working directory, without any of them switching the others' checkout out
from under them.

This is the "cwd handling" that 516ce5d was waiting on. That commit removed the
Claude/Codex/opencode targets from the new-tile menus because "launching a
terminal straight into Claude, Codex, or opencode left cwd switching messy and
unresolved", while deliberately keeping AGENT_TARGETS and the tile-manager
title-trust set in place. Worktrees give each agent terminal a stable, isolated
directory, which removes that blocker.

How it works

Resolution is three-case, because git allows a branch in only one worktree at a
time — the main working tree included:

  1. The branch already has a worktree (the main tree appears in
    git worktree list --porcelain, so this covers it) → reuse that path
  2. Creating a new branch → git worktree add -b <branch> <path> [<base>]
  3. Otherwise → git worktree add <path> <branch>

Worktrees live in ~/.collaborator/worktrees/<repo>-<hash>/<branch>/, namespaced
by a hash of the repository root in the same way ipc-endpoint.ts namespaces
sockets. Nothing is written inside the repository.

"Create new branch…" asks for a name, then offers the branch list again to pick a
base. Branching off main works even while main is checked out in the main
tree, since only the new branch gets checked out.

Design notes

  • The tile model is unchanged. A branch terminal is an ordinary terminal tile
    with a different cwd, so persistence, restore and titling all work with no
    changes — the worktree directory is named for the branch, so the title reads
    correctly for free.
  • No dead ends. Dismissing the branch picker opens a plain terminal in the
    workspace folder, exactly as "New terminal tile" does. Dismissing the base
    picker omits the start point, which is already git's default.
  • Closing a tile never removes a worktree. Uncommitted work can't be lost by
    closing a tile.
  • Existing creation paths (double-click, Cmd+N, drag-drop) are untouched.

Known limitations

  • A new worktree is unprovisioned. git worktree add checks out tracked
    files only, so node_modules, .env and build output are absent — in a fresh
    worktree of this repo, bun run dev fails until bun install is run there.
    This is inherent to worktrees and the manual workaround has the same problem;
    per-repository setup commands would be a natural follow-up.
  • Worktrees are never cleaned up. Deliberate: automated removal is how other
    tools have destroyed uncommitted work. Archive/delete lifecycle is a separate
    piece of work.
  • Concurrent git worktree add is not serialised. Git takes index.lock
    itself and fails the loser rather than corrupting anything, and that error
    surfaces in the dialog — acceptable for a user-initiated per-tile action, but
    it would need serialising before any bulk creation.

Scope

#66 proposes worktrees registered as workspaces. This implements them as a
tile's cwd instead, which gives the side-by-side view the issue itself asks for
("each worktree could be a distinct spatial region on the canvas") at a fraction
of the surface area, and doesn't need per-workspace canvas state (#32) as a
prerequisite. Nothing here blocks the workspace-level features later.

Testing

bun test src/main/git-worktree.test.ts — 17 tests: the four parsing helpers,
plus resolveWorktree against a real temporary repository covering main-tree
reuse, creation for an existing branch, path reuse, creation from an explicit
base and from HEAD, recovery from a deleted worktree directory, and rejection of
a duplicate branch name.

Based on dev rather than main, since that's where development is happening.

🤖 Generated with Claude Code

jpedrosa94 and others added 6 commits August 19, 2026 20:29
Three defects found in review of the branch-worktree implementation:

- git:list-branches had no try/catch, so a git failure rejected across
  IPC and left the menu item doing nothing at all: no tile, no dialog,
  no fallback. It now returns null, the signal the renderer already
  treats as "not a repository" and handles by opening a plain terminal.
- The git() helper used Node's 1MB default maxBuffer. git-replay-worker
  sets 50MB; for-each-ref on a repository with many thousands of
  branches exceeded the default and threw ENOBUFS.
- git keeps listing a worktree after its directory is deleted, until
  prune runs, so resolveWorktree could return a path that no longer
  exists. The terminal then relied on cwd-fallback and silently opened
  somewhere other than the branch the user picked.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The original plan tested only the pure parsing helpers, citing
git-replay-worker as precedent for leaving subprocess-backed code
uncovered. That was the wrong comparison: git-replay-worker builds a
cache where failure degrades a visualization, while resolveWorktree
decides which directory an agent writes code into. Every defect found
in review sat in the untested half.

Adds seven cases against a real temporary repository, following the
fixture pattern in cwd-fallback.test.ts: main-tree reuse, creation for
an existing branch, path reuse, creation from an explicit base and from
HEAD, recovery from a deleted worktree directory, and rejection of a
duplicate branch name.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant